/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.editor.controller;

import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.KeyStroke;
import javax.swing.text.Document;

import javax.swing.text.html.StyleSheet;
import offset.nodes.Constants;
import offset.nodes.UrlQueryConstants;
import offset.nodes.client.model.HttpUtils;
import offset.nodes.client.model.ServerModel;
import offset.nodes.client.editor.Editor;
import offset.nodes.client.editor.model.ComponentModel;
import offset.nodes.client.editor.model.DocumentContext;
import offset.nodes.client.editor.model.EditorDocument;
import offset.nodes.client.editor.model.EditorModel;
import offset.nodes.client.editor.model.HeadingTree;
import offset.nodes.client.editor.model.StructureTree;
import offset.nodes.client.editor.model.messages.CancelChanges;
import offset.nodes.client.editor.model.messages.UploadPage;
import offset.nodes.client.editor.view.AuthentifyUserDialog;
import offset.nodes.client.editor.view.ConcurrentModificationDialog;
import offset.nodes.client.editor.view.PopupTree.NodePopup;
import offset.nodes.client.editor.view.ServerErrorDialog;
import offset.nodes.client.model.CommentedUserRequest;

public class FileActions {

    static String additionalStyleSheet = null;

    public static String getAdditionalStyleSheet() {
        return additionalStyleSheet;
    }

    public static void setAdditionalStyleSheet(String additionalStyleSheet) {
        FileActions.additionalStyleSheet = additionalStyleSheet;
    }

    public static abstract class AbstractFileAction extends AbstractAction {

        Editor editor = null;
        Document document = null;

        public AbstractFileAction(String name, Editor editor) {
            this.editor = editor;
            putValue(NAME, name);
            putValue(SHORT_DESCRIPTION, name);
        }

        public Editor getEditor() {
            return editor;
        }

        public void setAccelerator(KeyStroke keystroke) {
            putValue(ACCELERATOR_KEY, keystroke);
        }

        public void setIcon(ImageIcon icon) {
            putValue(SMALL_ICON, icon);
        }

        protected void setupPane() {
            NodePopup[] headingNodePopups = editor.getHeading().getNodePopups();
            editor.setHeading(new HeadingTree(editor.getBrowserPane(), editor.getHeadingTree(), editor));

            NodePopup[] structureNodePopups = editor.getStructure().getNodePopups();
            editor.setStructure(new StructureTree(editor.getBrowserPane(), editor.getStructureTree(), editor.getProperties(), editor));

            if (document == null || editor.getBrowserPane().getDocument() != document) {
                document = editor.getBrowserPane().getDocument();
                editor.clearCaretListeners();
                editor.addUserCaretListener(editor.getStructure());
                editor.addUserCaretListener(editor.getHeading());
                editor.getBrowserPane().getDocument().addUndoableEditListener(editor.getUndoableEditListener());
                editor.getHeading().update();
                editor.getHeading().setNodePopups(headingNodePopups);
                editor.getStructure().update();
                editor.getStructure().setNodePopups(structureNodePopups);
            }
        }

        protected String toURLString(String pathName) {
            StringBuffer buf = new StringBuffer("file:");

            for (int i = 0; i < pathName.length(); i++) {
                if (pathName.charAt(i) == File.separatorChar)
                    buf.append("/");
                else
                    buf.append(pathName.charAt(i));
            }

            return buf.toString();
        }
    }

    public static class SaveServerAction extends AbstractFileAction {

        public SaveServerAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("File.saveServer"), editor);
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc_save2.png")));
        }

        public void actionPerformed(ActionEvent e) {
            runAction();
        }

        protected void sendToServer(UploadPage.Request request) {
            try {
                ServerModel model = new ServerModel(new URL((String) getEditor().getProperties().get(Editor.PROP_UPLOAD_TO)));
                UploadPage.Response response = (UploadPage.Response) model.sendRequest(request);

                if (response.getResult() == UploadPage.Response.RESULT_CONCURRENT_MODIFICATION) {
                    ConcurrentModificationDialog dialog = new ConcurrentModificationDialog(null, true);
                    if (dialog.getReturnStatus() == ConcurrentModificationDialog.RET_OK) {
                        request.setLastModifed(Calendar.getInstance());
                        response = (UploadPage.Response) model.sendRequest(request);
                        if (response.getResult() != UploadPage.Response.RESULT_SUCCESS)
                            new ServerErrorDialog(null, true);
                    }
                } else if (response.getResult() == UploadPage.Response.RESULT_SUCCESS) {
                    String returnTo = (String) getEditor().getProperties().get(Editor.PROP_RETURN_URL);
                    URL url = null;
                    if (returnTo != null)
                        url = new URL(returnTo);
                    else {
                        url = getEditor().getBrowserPane().getPage();
                        url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath());
                    }

                    url = new URL(HttpUtils.addParameter(
                            HttpUtils.removeParameter(url.toString(), UrlQueryConstants.PARAM_MODE),
                            Constants.PAR_RESULT,
                            Constants.PARVALUE_OK));

                    getEditor().getAppletContext().showDocument(url);
                }

            } catch (Exception ex) {
                ex.printStackTrace();
            }

        }

        public void runAction() {
            String path = (String) getEditor().getProperties().get(Editor.PROP_DOCUMENT_PATH);
            String upload = (String) getEditor().getProperties().get(Editor.PROP_UPLOAD_TO);
            if (upload == null)
                return;

            UploadPage.Request request = new UploadPage.Request();

            try {
                request.setPath(path);
                EditorDocument document = (EditorDocument) getEditor().getBrowserPane().getDocument();
                request.setText(document.getText(0, document.getLength()));
                request.setHtml(EditorModel.getInnerHTML("div", EditorModel.getInnerHTML("body", getEditor().getBrowserPane().getText())));
                
                // save components first
                ComponentModel componentModel = new ComponentModel(new DocumentContext(getEditor().getBrowserPane()), getEditor());
                componentModel.saveComponents(request);

                sendToServer(request);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class SaveServerCommentAction extends SaveServerAction {

        public SaveServerCommentAction(Editor editor) {
            super(editor);
            String name = java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("File.saveServerComment");
            putValue(NAME, name);
            putValue(SHORT_DESCRIPTION, name);
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc_saveComment.png")));
        }

        public void actionPerformed(ActionEvent e) {
            runAction(null);
        }

        public void runAction(CommentedUserRequest comment) {
            if (comment == null) {
                AuthentifyUserDialog userDialog = new AuthentifyUserDialog(null, true);
                userDialog.setLocation(100, 100);
                if (userDialog.showDialog() == AuthentifyUserDialog.CANCEL)
                    return;

                comment = new CommentedUserRequest();
                comment.setComment(userDialog.getComment());
            }

            String path = (String) getEditor().getProperties().get(Editor.PROP_DOCUMENT_PATH);
            String upload = (String) getEditor().getProperties().get(Editor.PROP_UPLOAD_TO);
            if (upload == null)
                return;

            UploadPage.Request request = new UploadPage.Request();

            try {
                if (comment.getComment() != null && comment.getComment().length() > 0)
                    request.setComment(comment.getComment());

                request.setPath(path);
                EditorDocument document = (EditorDocument) getEditor().getBrowserPane().getDocument();
                request.setText(document.getText(0, document.getLength()));
                request.setHtml(EditorModel.getInnerHTML("div", EditorModel.getInnerHTML("body", getEditor().getBrowserPane().getText())));
                
                // save components first
                ComponentModel componentModel = new ComponentModel(new DocumentContext(getEditor().getBrowserPane()), getEditor());
                componentModel.saveComponents(request);                

                sendToServer(request);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class NewFileAction extends AbstractFileAction {

        public NewFileAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("New_file"), editor);
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc05500.png")));
        }

        public void actionPerformed(ActionEvent e) {
            try {
                String html = "<html><head><link type=\"text/css\" rel=\"stylesheet\" href=\"" + editor.getBase() + "/main.css\"></head><body><p></p></body></html>";
                editor.getBrowserPane().setText(html);
                editor.getBrowserPane().setCaretPosition(0);
                editor.getBrowserPane().getDocument().addUndoableEditListener(
                        editor.getUndoableEditListener());
                editor.getBrowserPane().grabFocus();
                setupPane();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class AddStyleSheetAction extends AbstractFileAction {

        public AddStyleSheetAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("AddStyleSheet"), editor);
            //setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc05501.png")));
        }

        public void actionPerformed(ActionEvent e) {
            if (editor.getChooser() == null)
                editor.setChooser(new JFileChooser());
            if (editor.getChooser().showOpenDialog(editor.getBrowserPane()) != javax.swing.JFileChooser.APPROVE_OPTION)
                return;

            try {
                File selectedFile = editor.getChooser().getSelectedFile();
                byte[] buf = new byte[(int) selectedFile.length()];
                FileInputStream in = new FileInputStream(selectedFile);
                in.read(buf);
                in.close();
                setAdditionalStyleSheet(new String(buf));

                InputStreamReader reader = new InputStreamReader(new FileInputStream(selectedFile));
                StyleSheet stylesheet = ((EditorDocument) editor.getBrowserPane().getDocument()).getStyleSheet();
                stylesheet.loadRules(reader, null);
                reader.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class OpenFileAction extends AbstractFileAction {

        public OpenFileAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Open_file_..."), editor);
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc05501.png")));
        }

        public void actionPerformed(ActionEvent e) {
            if (editor.getChooser().showOpenDialog(editor.getBrowserPane()) != javax.swing.JFileChooser.APPROVE_OPTION)
                return;

            try {
                File selectedFile = editor.getChooser().getSelectedFile();
                String upload = (String) editor.getProperties().get(Editor.PROP_UPLOAD_TO); // to allow loading offline files and saving them to the server, we store the servers upload address
                String documentPath = (String) editor.getProperties().get(Editor.PROP_DOCUMENT_PATH); // to allow loading offline files and saving them to the server, we store the servers upload address

                editor.getBrowserPane().setPage(new URL(toURLString(selectedFile.getPath())));
                String name = selectedFile.getName();
                int dot = name.indexOf(".");
                if (dot > 0)
                    name = name.substring(0, dot);
                //editor.setTitle( name);
                setupPane();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class SaveFileAction extends AbstractFileAction {

        public SaveFileAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Save_file"), editor);
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc05505.png")));
            setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.SHIFT_MASK | java.awt.event.InputEvent.CTRL_MASK));
        }

        public void actionPerformed(ActionEvent e) {
            try {
                FileOutputStream out = new FileOutputStream(editor.getChooser().getSelectedFile());
                out.write(editor.getBrowserPane().getText().getBytes());
                out.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class SaveFileAsAction extends AbstractFileAction {

        public SaveFileAsAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Save_file_as_..."), editor);
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc05502.png")));
        }

        public void actionPerformed(ActionEvent e) {
            if (editor.getChooser().showOpenDialog(editor.getBrowserPane()) != javax.swing.JFileChooser.APPROVE_OPTION)
                return;

            try {
                FileOutputStream out = new FileOutputStream(editor.getChooser().getSelectedFile());
                out.write(editor.getBrowserPane().getText().getBytes());
                out.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class ExitAction extends AbstractFileAction {

        public ExitAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("File.exit"), editor);
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc_exit2.png")));
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                String upload = (String) editor.getProperties().get(Editor.PROP_UPLOAD_TO);
                if (upload != null) {
                    ServerModel model = new ServerModel(new URL(upload));
                    try {
                        // handle the components
                        ComponentModel componentModel = new ComponentModel(new DocumentContext(getEditor().getBrowserPane()), getEditor());
                        componentModel.exitComponents();
                        
                        CancelChanges.Response response = (CancelChanges.Response) model.sendRequest(new CancelChanges.Request());
                    } catch (Exception ex) {
                        Logger.getLogger(FileActions.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }

                String returnTo = (String) editor.getProperties().get(Editor.PROP_RETURN_URL);
                URL url = null;
                if (returnTo != null)
                    url = new URL(returnTo);
                else
                    url = editor.getBrowserPane().getPage();

                url = new URL(HttpUtils.addParameter(
                        HttpUtils.removeParameter(url.toString(), UrlQueryConstants.PARAM_MODE),
                        Constants.PAR_RESULT,
                        Constants.PARVALUE_CANCEL));

                if (url != null) {
                    System.out.println(url);
                    editor.getAppletContext().showDocument(url);
                }
            } catch (MalformedURLException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * Backup the current state. 
     * 
     * As an editor variable will be used, the current state will become accessible externally as a side effect.
     */
    public static class BackupAction extends AbstractFileAction {

        public BackupAction(Editor editor) {
            super("", editor);
        }

        public String getHtml() {
            return EditorModel.getInnerHTML("body", editor.getBrowserPane().getText());
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            editor.getResult().setValue(getHtml());
        }
    }

    /**
     * Restore the current state to some earlier backup, if there is any
     */
    public static class RestoreAction extends BackupAction {

        public RestoreAction(Editor editor) {
            super(editor);
        }

        protected void setupBrowserPane() {
            getEditor().getBrowserPane().getDocument().addUndoableEditListener(editor.getUndoableEditListener());
            getEditor().getHeading().update();
            getEditor().getStructure().update();
        }
        
        protected void detachEvents() {
            getEditor().getBrowserPane().getDocument().removeUndoableEditListener(editor.getUndoableEditListener());
            getEditor().getHeading().detachFromDocument();
            getEditor().getStructure().detachFromDocument();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (editor.getResult().getValue() != null) {
                if (getHtml().equals(editor.getResult().getValue()))
                    return;
                detachEvents();
                
                editor.getBrowserPane().setText((String) editor.getResult().getValue());
                setupBrowserPane();
                getEditor().getBrowserPane().setCaretPosition(0);
                getEditor().getBrowserPane().repaint();
            }
        }
    }
    
    /**
     * Invoke the ok button of the component container.
     */
    public static class ContainerOkAction extends AbstractFileAction {
        public ContainerOkAction(Editor editor) {
            super("", editor);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getEditor().getComponentContainer().ok(); 
        }

    }

    /**
     * Invoke the cancel button of the component container.
     */
    public static class ContainerCancelAction extends AbstractFileAction {
        public ContainerCancelAction(Editor editor) {
            super("", editor);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getEditor().getComponentContainer().cancel();
        }

    }    
}
